//
//  InMemoryBitmap.m
//  ToneMapper
//
//  Created by Andrey on 03/02/2009.
//  Copyright 2009 Karma Software. All rights reserved.
//

#import "InMemoryBitmap.h"
#include <ImfCRgbaFile.h>

@implementation InMemoryBitmap

@synthesize width;
@synthesize height;
@synthesize pixelData;

- (id) init
{
	if (self = [super init])
	{
		pixelData = nil;
		width = height = 0;
		_clampingLuminance = _scalingFactor = 1.0;
		
		maxLuminance = 1e-30;
		minLuminance = 1e30;
		
		needRecomputeHistogram = true;
		histogram = (int*)malloc(sizeof(int) * HISTOGRAM_STEP_COUNT);
		memset(histogram, 0, sizeof(int) * HISTOGRAM_STEP_COUNT);
	}
	
	return self;
}

- (id) initWithWidth: (NSUInteger)w AndHeight: (NSUInteger)h
{
	if (self = [super init])
	{
		width = w;
		height = h;
		pixelData = (BitmapPixel*)malloc(width * height * sizeof(BitmapPixel));
		_clampingLuminance = _scalingFactor = 1.0;
		
		maxLuminance = 1e-30;
		minLuminance = 1e30;
		
		needRecomputeHistogram = true;
		histogram = (int*)malloc(sizeof(int) * HISTOGRAM_STEP_COUNT);
		memset(histogram, 0, sizeof(int) * HISTOGRAM_STEP_COUNT);
	}
	
	return self;
}

- (id) initWithBitmap: (InMemoryBitmap*) bitmap
{
	[self initWithWidth:bitmap.width AndHeight:bitmap.height];
	
	if (self)
	{
		memcpy(pixelData, [bitmap pixelData], width * height * sizeof(BitmapPixel));
	}
	
	return self;
}

- (id) initWithOpenExrFile: (NSString*)filename
{
	if (self = [super init])
	{
		int minX, minY, maxX, maxY;
		ImfRgba* pixels;
		ImfInputFile* inputFile = ImfOpenInputFile([filename UTF8String]);
		const ImfHeader* fileHeader = ImfInputHeader(inputFile);
				
		/* Read data window dimensions */
		ImfHeaderDataWindow(fileHeader, &minX, &minY, &maxX, &maxY);
		height = maxY - minY + 1;
		width = maxX - minX + 1;
		
		/* Allocate framebuffer */
		pixels = (ImfRgba*)malloc(width * height * sizeof(ImfRgba));
		
		/* Set framebuffer and read pixels */
		ImfInputSetFrameBuffer(inputFile, pixels - minX - width * minY, 1, width);
		ImfInputReadPixels(inputFile, minY, maxY);
		
		/* Copy file pixels to bitmap */
		maxLuminance = 1e-30;
		minLuminance = 1e30;
		pixelData = (BitmapPixel*)malloc(width * height * sizeof(BitmapPixel));
		
		for (int y = 0; y < height; y++)
		{
			for (int x = 0; x < width; x++)
			{
				int index = y * width + x;
				
				ImfHalfToFloatArray(4, 
									(ImfHalf*)(&pixels[index]), 
									(CGFloat*)(&pixelData[index]));
	/*			
				CGFloat luminance = 0.3 * pixelData[index].red +
									0.59 * pixelData[index].green +
									0.11 * pixelData[index].blue;
	*/
				CGFloat luminance = (pixelData[index].red +
									pixelData[index].green +
									pixelData[index].blue) / 3;
	//			luminance = log10(luminance);
				
				pixelData[index].luminance = luminance;
				
				if (pixelData[index].luminance > maxLuminance)
					maxLuminance = pixelData[index].luminance;
				
				if (pixelData[index].luminance < minLuminance)
					minLuminance = pixelData[index].luminance;
			}
		}
		
		free(pixels);
		ImfCloseInputFile(inputFile);
		
		_clampingLuminance = _scalingFactor = 1.0;
		
		needRecomputeHistogram = true;
		histogram = (int*)malloc(sizeof(int) * HISTOGRAM_STEP_COUNT);
		memset(histogram, 0, sizeof(int) * HISTOGRAM_STEP_COUNT);
	}
	
	return self;
}

- (id) initWithFile: (NSString*)filename
{
	if (self = [super init])
	{
		NSString* urlPrefix = @"file://";
		NSString* urlFromFilename = [urlPrefix stringByAppendingString:filename];
		NSBitmapImageRep* bitmapRep = [NSBitmapImageRep imageRepWithContentsOfURL:[NSURL URLWithString:urlFromFilename]];
		width = [bitmapRep pixelsWide];
		height = [bitmapRep pixelsHigh];
		
		/* Copy file pixels to bitmap */
		maxLuminance = 1e-30;
		minLuminance = 1e30;
		pixelData = (BitmapPixel*)malloc(width * height * sizeof(BitmapPixel));
		
		for (int x = 0; x < width; x++)
		{
			for (int y = 0; y < height; y++)
			{
				NSUInteger rawPixelValues[4];
				
				[bitmapRep getPixel:rawPixelValues atX:x y:y];
				
				int index = y * width + x;
				pixelData[index].red	= rawPixelValues[0];
				pixelData[index].green	= rawPixelValues[1];
				pixelData[index].blue	= rawPixelValues[2];
				pixelData[index].alpha	= 255;
/*				
				pixelData[index].luminance = (int)((pixelData[index].red +
											  pixelData[index].green +
											  pixelData[index].blue) / 3 + 0.5);
 */
				pixelData[index].luminance = (int)(0.3 * pixelData[index].red +
											0.59 * pixelData[index].green +
											0.11 * pixelData[index].blue + 0.5);
			}
		}
		
		_clampingLuminance = _scalingFactor = 1.0;
		
		needRecomputeHistogram = true;
		histogram = (int*)malloc(sizeof(int) * HISTOGRAM_STEP_COUNT);
		memset(histogram, 0, sizeof(int) * HISTOGRAM_STEP_COUNT);
	}
	
	return self;
}

- (BitmapPixel) getPixelAtX: (NSUInteger)x Y: (NSUInteger)y
{
	int index = y * width + x;
	BitmapPixel pixel = pixelData[index];

	return pixel;
}

- (void) setPixelTo: (BitmapPixel)pixel AtX: (NSUInteger)x Y: (NSUInteger)y
{
	int index = y * width + x;
	
	pixelData[index] = pixel;
	
	pixelData[index].luminance = 0.3 * pixelData[index].red +
								0.59 * pixelData[index].green +
								0.11 * pixelData[index].blue;
	
	needRecomputeHistogram = true;
}

- (void) multiplyPixels: (InMemoryBitmap*) bitmap
{
	if (width != bitmap.width || height != bitmap.height)
		return;
	
	for (int x = 0; x < bitmap.width; x++)
	{
		for (int y = 0; y < bitmap.height; y++)
		{
			BitmapPixel pixel = [self getPixelAtX:x Y:y];
			BitmapPixel maskPixel = [bitmap getPixelAtX:x Y:y];
			
			pixel.red *= maskPixel.red;
			pixel.green *= maskPixel.green;
			pixel.blue *= maskPixel.blue;
			
			[self setPixelTo:pixel AtX:x Y:y];
		}
	}
}

- (int*) getHistogram
{
/*	
	if (needRecomputeHistogram)
	{
		CGFloat histogramStep;
		histogramStep = (maxLuminance - minLuminance) / HISTOGRAM_STEP_COUNT;
		memset(histogram, 0, sizeof(int) * HISTOGRAM_STEP_COUNT);
		
		for (int x = 0; x < width; x++)
		{
			for (int y = 0; y < height; y++)
			{
				BitmapPixel pixel = [self getPixelAtX:x Y:y];
				int step = (int)(pixel.luminance / histogramStep);
				
				if (step >= HISTOGRAM_STEP_COUNT)
					step = HISTOGRAM_STEP_COUNT - 1;
				
				histogram[step]++;
			}
		}
		
		needRecomputeHistogram = false;
	}
	
	return histogram;
*/	
	if (needRecomputeHistogram)
	{
		memset(histogram, 0, sizeof(int) * HISTOGRAM_STEP_COUNT);
		
		for (int x = 0; x < width; x++)
		{
			for (int y = 0; y < height; y++)
			{
				BitmapPixel pixel = [self getPixelAtX:x Y:y];
						
				histogram[(int)pixel.luminance]++;
			}
		}
		
		needRecomputeHistogram = false;
	}
	
	return histogram;	
}

- (void) scalePixels: (CGFloat)factor
{
	_scalingFactor = factor;
}

- (void) clampPixels: (CGFloat)clampingLuminance
{
	_clampingLuminance = clampingLuminance;
}

- (void) dealloc
{
	if (pixelData != nil)
		free(pixelData);
	
	if (histogram != nil)
		free(histogram);
	
	[super dealloc];
}

@end
